package io.github.generator.engine;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.TemplateLoadWay;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import io.github.common.consts.StringPool;
import io.github.common.util.StringUtils;
import io.github.generator.loader.SpringResourceLoaderEngine;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;

/**
 * 自定义应用程序模板引擎
 *
 * @author haijun
 * @version 1.0.0
 * @email "mailto:haijun@qq.com"
 * @date 2024.11.06 20:44
 * @since 1.0.0
 */
@Slf4j
public class ApplicationVelocityEngine extends VelocityTemplateEngine {


    /**
     * Init
     *
     * @param configBuilder config builder
     * @return the velocity template engine
     * @since 1.0.0
     */
    @Override
    public VelocityTemplateEngine init(ConfigBuilder configBuilder) {
        try {
            Field field = this.getClass().getSuperclass().getDeclaredField("velocityEngine");
            Object velocityEngineObj = FieldUtils.readField(field, this, true);
            if (velocityEngineObj == null) {
                Properties p = this.getProperties(configBuilder);
                velocityEngineObj = new VelocityEngine(p);
            }
            FieldUtils.writeField(field, this, velocityEngineObj, true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return this;
    }

    /**
     * Output custom file
     *
     * @param customFiles custom files
     * @param tableInfo   table info
     * @param objectMap   object map
     * @since 1.0.0
     */
    @Override
    protected void outputCustomFile(List<CustomFile> customFiles, TableInfo tableInfo, Map<String, Object> objectMap) {
        String entityName = tableInfo.getEntityName();
        String parentPath = this.getPathInfo(OutputFile.parent);
        customFiles.forEach(file -> {
            String filePath = StringUtils.isNotBlank(file.getFilePath()) ? file.getFilePath() : parentPath;
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", com.baomidou.mybatisplus.core.toolkit.StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = file.getFileName();
            objectMap.put(fileName + "Name", tableInfo.getEntityName() + fileName);

            fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + fileName;
            String packagePath = this.getConfigBuilder().getPackageConfig().getParent() + StringPool.DOT + file.getPackageName();
            objectMap.put(file.getFileName() + "Package", packagePath);

            if (!StringUtils.endsWith(fileName, ".java")) {
                fileName += StringPool.DOT + "java";
            }
            this.outputFile(new File(fileName), objectMap, file.getTemplatePath(), file.isFileOverride());
        });
    }

    /**
     * 排除枚举字段
     *
     * @param tableInfo table info
     * @return the list
     * @since 1.0.0
     */
    public void excludeEnumField(TableInfo tableInfo, Map<String, Object> objectMap) {
        Object enumConfig = objectMap.get("enumConfig");
        if (CollectionUtil.isNotEmpty((List<?>) enumConfig)) {
            return;
        }
        List<TableField> tableFields = new LinkedList<>();
        List<EnumConfig> enumConfigs = new ArrayList<>();
        for (TableField field : tableInfo.getFields()) {
            String comment = field.getComment();
            if (StringUtils.isEmpty(comment)) {
                continue;
            }
            // 使用自定义枚举
            boolean customEnumerations = StringUtils.startsWith(comment, "自定义枚举");
            // 使用系统枚举
            boolean systemEnumerations = StringUtils.startsWith(comment, "系统枚举");
            if (customEnumerations || systemEnumerations) {
                EnumConfig config = new EnumConfig();
                String[] split = StringUtils.split(comment, StringPool.COLON);
                String enumName = split[1];
                config.setFileName(enumName);
                String content = split[2];
                if (StringUtils.isNotBlank(comment)) {
                    List<String> list = Arrays.asList(StringUtils.split(content, StringPool.SEMICOLON));
                    config.setContent(list);
                }
            } else {
                tableFields.add(field);
            }
        }
        objectMap.put("enumConfig", enumConfigs);
        try {
            FieldUtils.writeDeclaredField(tableFields, "fields", tableFields);
        } catch (IllegalAccessException e) {
            log.error("枚举字段替换失败！！！");
        }
    }

    /**
     * Gets properties *
     *
     * @param configBuilder config builder
     * @return the properties
     * @since 1.0.0
     */
    private Properties getProperties(ConfigBuilder configBuilder) {
        Properties p = new Properties();
        p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
        p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
        if (configBuilder.getTemplateLoadWay().isFile()) {
            // 文件模板
            p.setProperty(ConstVal.VM_LOAD_PATH_KEY, SpringResourceLoaderEngine.class.getName());
            p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY);
            p.setProperty("file.resource.loader.unicode", StringPool.TRUE);
        } else {
            // 文本模板
            p.setProperty(Velocity.RESOURCE_LOADER, TemplateLoadWay.STRING.getValue());
        }
        return p;
    }

    /**
     * 枚举配置信息
     *
     * @author zhonghaijun
     * @version 1.0.0
     * @email "mailto:zhonghaijun@zhxx.com"
     * @date 2024.11.22 09:33
     * @since 1.0.0
     */
    @Data
    public static class EnumConfig {
        /** 枚举的内容 */
        private List<String> content;

        /** 文件名称 */
        private String fileName;
    }
}
